#pragma once
#include "event/mutex.hpp"
#include "event/scopedlock.hpp"
#include "designpattern/singletonholder.hpp"
using namespace JHCPP::designpattern;
#include "thread/activeresult.hpp"
#include "thread/activemethod.hpp"
using namespace JHCPP::thread;
#include "utils/shareptr.hpp"
using namespace JHCPP::utils;


namespace JHCPP
{
	namespace event
	{
		template <class TArgs, class TStrategy, class TDelegate, class TMutex = CMutex> 
		class TAbstractEvent
		{
		public:
			typedef TArgs Args;

			TAbstractEvent() : m_executeAsync(this, &TAbstractEvent::executeAsyncImpl), m_enabled(true)
			{
			}

			TAbstractEvent(const TStrategy& strat) : m_executeAsync(this, &TAbstractEvent::executeAsyncImpl), m_strategy(strat), m_enabled(true)
			{	
			}

			virtual ~TAbstractEvent()
			{
			}

			void operator += (const TDelegate& aDelegate)
			{
				typename TMutex::ScopedLock lock(m_mutex);
				m_strategy.add(aDelegate);
			}

			void operator -= (const TDelegate& aDelegate)
			{
				typename TMutex::ScopedLock lock(m_mutex);
				m_strategy.remove(aDelegate);
			}

			void operator () (const void* pSender, TArgs& args)
			{
				notify(pSender, args);
			}

			void operator () (TArgs& args)
			{
				notify(0, args);
			}

			void notify(const void* pSender, TArgs& args)
			{
				TScopedLockWithUnlock<TMutex> lock(m_mutex);

				if (!m_enabled) return;

				TStrategy strategy(m_strategy);
				lock.unlock();
				strategy.notify(pSender, args);
			}

			TActiveResult<TArgs> notifyAsync(const void* pSender, const TArgs& args)
			{
				NotifyAsyncParams params(pSender, args);
				{
					typename TMutex::ScopedLock lock(m_mutex);

					params.ptrStrat = TSharedPtr<TStrategy>(new TStrategy(m_strategy));
					params.enabled  = m_enabled;
				}
				TActiveResult<TArgs> result = m_executeAsync(params);
				return result;
			}

			void enable()
			{
				typename TMutex::ScopedLock lock(m_mutex);
				m_enabled = true;
			}

			void disable()
			{
				typename TMutex::ScopedLock lock(m_mutex);
				m_enabled = false;
			}

			bool isEnabled() const
			{
				typename TMutex::ScopedLock lock(m_mutex);
				return m_enabled;
			}

			void clear()
			{
				typename TMutex::ScopedLock lock(m_mutex);
				m_strategy.clear();
			}

			bool empty() const
			{
				typename TMutex::ScopedLock lock(m_mutex);
				return m_strategy.empty();
			}

		protected:
			struct NotifyAsyncParams
			{
				TSharedPtr<TStrategy> ptrStrat;
				const void* pSender;
				TArgs       args;
				bool        enabled;

				NotifyAsyncParams(const void* pSend, const TArgs& a):ptrStrat(), pSender(pSend), args(a), enabled(true)
				{
				}
			};

			TActiveMethod<TArgs, NotifyAsyncParams, TAbstractEvent> m_executeAsync;

			TArgs executeAsyncImpl(const NotifyAsyncParams& par)
			{
				if (!par.enabled)
				{
					return par.args;
				}

				NotifyAsyncParams params = par;
				TArgs retArgs(params.args);
				params.ptrStrat->notify(params.pSender, retArgs);
				return retArgs;
			}

			TStrategy m_strategy; 
			bool      m_enabled;  
			mutable TMutex m_mutex;

		private:
			TAbstractEvent(const TAbstractEvent& other);
			TAbstractEvent& operator = (const TAbstractEvent& other);
		};
	}//end of namespace event
}//end of namespace JHCPP
